package com.simple.mqtt.common.tool;

import java.util.*;
import java.util.function.Function;

public class CollUtil {
    private CollUtil() {
    }

    public static boolean isEmpty(Collection<?> collection) {
        return collection == null || collection.isEmpty();
    }

    public static boolean isNotEmpty(Collection<?> collection) {
        return !isEmpty(collection);
    }

    public static <K, V> V computeIfAbsent(Map<K, V> map, K key, Function<K, V> mappingFunction) {
        V value = map.get(key);
        return value != null ? value : map.computeIfAbsent(key, mappingFunction);
    }

    public static <T> List<List<T>> partition(List<T> list, int size) {
        Objects.requireNonNull(list, "List to partition must not null.");
        if (size < 1) {
            throw new IllegalArgumentException("List to partition size must more then zero.");
        } else {
            return (List) (list instanceof RandomAccess ? new RandomAccessPartition(list, size) : new Partition(list, size));
        }
    }

    private static class Partition<T> extends AbstractList<List<T>> {
        final List<T> list;
        final int size;

        Partition(List<T> list, int size) {
            this.list = list;
            this.size = size;
        }

        @Override
        public List<T> get(int index) {
            if (index >= 0 && index < this.size()) {
                int start = index * this.size;
                int end = Math.min(start + this.size, this.list.size());
                return this.list.subList(start, end);
            } else {
                throw new IndexOutOfBoundsException(String.format("index (%s) must be less than size (%s)", index, this.size()));
            }
        }

        @Override
        public int size() {
            return ceilDiv(this.list.size(), this.size);
        }

        @Override
        public boolean isEmpty() {
            return this.list.isEmpty();
        }

        private static int ceilDiv(int x, int y) {
            int r = x / y;
            if (r * y < x) {
                ++r;
            }

            return r;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            } else if (o != null && this.getClass() == o.getClass()) {
                if (!super.equals(o)) {
                    return false;
                } else {
                    Partition<?> partition = (Partition) o;
                    return Objects.equals(this.list, partition.list);
                }
            } else {
                return false;
            }
        }

        @Override
        public int hashCode() {
            return Objects.hash(new Object[]{super.hashCode(), this.list});
        }
    }

    private static class RandomAccessPartition<T> extends Partition<T> implements RandomAccess {
        RandomAccessPartition(List<T> list, int size) {
            super(list, size);
        }
    }
}